﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using BlawkayEngine;
using BlawkayEngine.Input;
using Microsoft.Xna.Framework.Input;
using BlawkayEngine.ScreenSystem;

namespace BlawkayHockey
{
    public class Person
    {
        public Person(Game oGame, Vector3 oPosition, bool oAI)
        {
            _IsAI = oAI;
            _Sides = new List<Quad>();

            Texture2D oTexture = oGame.Content.Load<Texture2D>("alphablockey");
            int a = 128;
            float offset = 7.5f;

            // Front
            _Sides.Add(new Quad(oGame, oTexture, new Rectangle(a * 0, a * 1, a * 1, a * 3), Vector3.Zero,
                new Vector3(0, offset, 2.5f), VectorHelper.GLOBAL_BACKWARD, VectorHelper.GLOBAL_UP, 5, 15, false));

            // Back
            _Sides.Add(new Quad(oGame, oTexture, new Rectangle(a * 2, a * 1, a * 1, a * 3), Vector3.Zero,
                new Vector3(0, offset, -2.5f), -VectorHelper.GLOBAL_BACKWARD, VectorHelper.GLOBAL_UP, 5, 15, false));

            // Right
            _Sides.Add(new Quad(oGame, oTexture, new Rectangle(a * 1, a * 1, a * 1, a * 3), Vector3.Zero,
                new Vector3(2.5f, offset, 0), VectorHelper.GLOBAL_RIGHT, VectorHelper.GLOBAL_UP, 5, 15, false));

            // Left
            _Sides.Add(new Quad(oGame, oTexture, new Rectangle(a * 3, a * 1, a * 1, a * 3), Vector3.Zero,
                new Vector3(-2.5f, offset, 0), -VectorHelper.GLOBAL_RIGHT, VectorHelper.GLOBAL_UP, 5, 15, false));

            // Top
            _Sides.Add(new Quad(oGame, oTexture, new Rectangle(a * 0, a * 0, a * 1, a * 1), Vector3.Zero,
                new Vector3(0, -7.5f + offset, 0), VectorHelper.GLOBAL_UP, VectorHelper.GLOBAL_RIGHT, 5, 5, false));

            // Bottom
            _Sides.Add(new Quad(oGame, oTexture, new Rectangle(a * 1, a * 0, a * 1, a * 1), Vector3.Zero,
                new Vector3(0, 7.5f + offset, 0), -VectorHelper.GLOBAL_UP, VectorHelper.GLOBAL_BACKWARD, 5, 5, false));

            // Shadow
            _Shadow = new Quad(oGame, oTexture, new Rectangle(a * 2, a * 0, a * 1, a * 1), Vector3.Zero,
                new Vector3(0, -7.5f + offset, 0), VectorHelper.GLOBAL_UP, VectorHelper.GLOBAL_RIGHT, 10, 10, false);
            _Shadow.Effect.LightingEnabled = false;

            _Velocity = Vector3.Zero;
            _Position = oPosition;
            _Direction = Vector3.Backward; _CurrentDirection = _Direction;
            UpdateQuads();
        }

        #region Methods

        public void Update(Camera oCamera)
        {
            if (GameInfo.IsDebug)
            {

            }

            if (Networking.IsSessionRunning)
            {
                if (Networking.Session.IsHost)
                {
                    Input(oCamera);
                    MoveBy(_Velocity);
                }
                else
                {
                    GetDirection(oCamera);
                }
            }
            else
            {
                Input(oCamera);
                MoveBy(_Velocity);
            }


            SmoothRotation();
            UpdateQuads();
            foreach (Quad q in _Sides)
                q.Update(oCamera);
            _Shadow.Update(oCamera);
        }

        public void DrawOpaque(Game oGame)
        {
            foreach (Quad q in _Sides)
                q.Draw(oGame);
        }

        public void DrawAlpha(Game oGame)
        {
            _Shadow.Draw(oGame);
        }

        private void MoveBy(Vector3 oShift)
        {
            _Position += oShift;
        }
        private void MoveTo(Vector3 oNewPosition)
        {
            _Position = oNewPosition;
        }
        private void Input(Camera oCamera)
        {
            if (_IsRemote == false)
            {
                if (_IsAI)
                {
                }
                else
                {
                    KeyboardInput oInput = GameInfo.KeyboardInput;
                    Vector3 oForward = VectorHelper.FlatDirection(oCamera.CameraForward);
                    Vector3 oRight = VectorHelper.FlatDirection(oCamera.CameraRight);
                    Vector3 oDirection = Vector3.Zero;

                    if (oInput.IsOnePressed(Keys.Up))
                        oDirection += oForward;
                    else if (oInput.IsOnePressed(Keys.Down))
                        oDirection += -oForward;

                    if (oInput.IsOnePressed(Keys.Left))
                        oDirection += -oRight;
                    else if (oInput.IsOnePressed(Keys.Right))
                        oDirection += oRight;

                    UpdateVelocity(oDirection);
                }
            }
            else
            {
                UpdateVelocity(_Direction);
            }


        }


        void GetDirection(Camera oCamera)
        {
            KeyboardInput oInput = GameInfo.KeyboardInput;
            Vector3 oForward = VectorHelper.FlatDirection(oCamera.CameraForward);
            Vector3 oRight = VectorHelper.FlatDirection(oCamera.CameraRight);
            Vector3 oDirection = Vector3.Zero;

            if (oInput.IsOnePressed(Keys.Up))
                oDirection += oForward;
            else if (oInput.IsOnePressed(Keys.Down))
                oDirection += -oForward;

            if (oInput.IsOnePressed(Keys.Left))
                oDirection += -oRight;
            else if (oInput.IsOnePressed(Keys.Right))
                oDirection += oRight;

            if (oDirection != Vector3.Zero)
            {
                oDirection.Normalize();

                if (float.IsNaN(_Direction.X) || float.IsNaN(_Direction.Y) || float.IsNaN(_Direction.Z))
                    oDirection = Vector3.Zero;
                else if (float.IsInfinity(_Direction.X) || float.IsInfinity(_Direction.Y) || float.IsInfinity(_Direction.Z))
                    oDirection = Vector3.Zero;
            }

            _Direction = oDirection;
        }


        void UpdateVelocity(Vector3 oDirection)
        {
            if (oDirection != Vector3.Zero)
            {
                _Direction = oDirection;
                _Direction.Normalize();

                if (float.IsNaN(_Direction.X) || float.IsNaN(_Direction.Y) || float.IsNaN(_Direction.Z))
                    _Direction = Vector3.Zero;
                else if (float.IsInfinity(_Direction.X) || float.IsInfinity(_Direction.Y) || float.IsInfinity(_Direction.Z))
                    _Direction = Vector3.Zero;

                _Velocity += _Direction * 2 * (1f / 60f);
                _Velocity *= 0.975f;
                _Direction.Z *= -1;
            }
            else
            {
                _Velocity *= 0.975f;
            }
        }

        private void SmoothRotation()
        {
            _CurrentDirection = Vector3.Lerp(_CurrentDirection, _Direction, 0.1f);
        }
        private void UpdateQuads()
        {

            foreach (Quad q in _Sides)
            {
                q.Position = _Position;
                q.Direction = _CurrentDirection;
            }

            _Shadow.Position = _Position;
        }

        public static void CheckCollision(ref Person oPersonA, ref Person oPersonB)
        {
            Vector2 oPosA = new Vector2(oPersonA._Position.X, oPersonA._Position.Z);
            Vector2 oPosB = new Vector2(oPersonB._Position.X, oPersonB._Position.Z);

            float oDistance = Vector2.Distance(oPosA, oPosB);
            float oMaxDistance = oPersonA._Radius + oPersonB._Radius;

            if (oDistance < oMaxDistance)
            {
                Vector3 oVelA = oPersonA._Velocity;
                Vector3 oVelB = oPersonB._Velocity;

                oPersonA._Velocity = oVelB;
                oPersonB._Velocity = oVelA;

                Vector2 oCenter = (oPosA + oPosB) * 0.5f;
                Vector2 oCenterToA = Vector2.Normalize(oPosA - oCenter);
                Vector2 oCenterToB = Vector2.Normalize(oPosB - oCenter);

                oPosA = oCenter + (oCenterToA * oPersonA._Radius);
                oPosB = oCenter + (oCenterToB * oPersonB._Radius);

                oPersonA._Position = new Vector3(oPosA.X, oPersonA._Position.Y, oPosA.Y);
                oPersonB._Position = new Vector3(oPosB.X, oPersonB._Position.Y, oPosB.Y);
            }
        }
        public static void CheckCollision(ref Person oPersonA, Arena oArena)
        {
            Rectangle oRectPerson = GetCollisionRectangle(oPersonA);
            Rectangle oRectArena = oArena.Bounds();
            //oRectArena.Offset(-oRectArena.Center.X, -oRectArena.Center.Y);

            if (oRectArena.Contains(oRectPerson) == false)
            {
                if (oRectPerson.Left < oRectArena.Left)
                {
                    oPersonA._Velocity.X = Math.Abs(oPersonA.Velocity.X);
                    oPersonA._Position.X = oRectArena.Left + oPersonA.Radius;
                }
                else if (oRectPerson.Right > oRectArena.Right)
                {
                    oPersonA._Velocity.X = -Math.Abs(oPersonA.Velocity.X);
                    oPersonA._Position.X = oRectArena.Right - oPersonA.Radius;
                }

                if (oRectPerson.Top < oRectArena.Top)
                {
                    oPersonA._Velocity.Z = Math.Abs(oPersonA.Velocity.Z);
                    oPersonA._Position.Z = oRectArena.Top + oPersonA.Radius;
                }
                else if (oRectPerson.Bottom > oRectArena.Bottom)
                {
                    oPersonA._Velocity.Z = -Math.Abs(oPersonA.Velocity.Z);
                    oPersonA._Position.Z = oRectArena.Bottom - oPersonA.Radius;
                }
            }
        }

        public static Rectangle GetCollisionRectangle(Person oPerson)
        {
            Vector2 oPos = new Vector2(oPerson.Position.X, oPerson.Position.Z);
            int oRadius = (int)(oPerson.Radius * Math.Sqrt(2));

            int x = (int)oPos.X - oRadius;
            int z = (int)oPos.Y - oRadius;
            int w = (int)oPos.X + oRadius - x;

            return new Rectangle(x, z, w, w);
        }

        #endregion

        public void SetDirection(Vector3 oDirection)
        {
            _Direction = oDirection;
        }

        public void SetPosition(Vector3 oPosition)
        {
            _Position = oPosition;
        }

        bool _IsRemote = false;
        public void SetRemote(bool oRemote)
        {
            _IsRemote = oRemote;
        }

        #region Properties

        public Vector3 Position
        {
            get { return _Position; }
        }

        public Vector3 Velocity
        {
            get { return _Velocity; }
        }

        public Vector3 Direction
        {
            get { return _Direction; }
        }

        public float Radius
        {
            get { return _Radius; }
        }

        #endregion

        #region Fields

        bool _IsAI;

        Vector3 _Velocity;
        Vector3 _Position;

        Vector3 _CurrentDirection;
        Vector3 _Direction;

        List<Quad> _Sides;

        Quad _Shadow;

        float _Radius = 4;

        #endregion

    }

}
